#include "interrupt.h"
#include "../lib/kernel/print.h"
#include "../thread/thread.h"
#include "debug.h"

static uint32_t ticks = 0;
void iclock(void)
{
	asm volatile("pusha;\
		push %ds;\
		push %es;\
		push %ss");
	struct task_struct * current_ts;
	GET_TASK_STRUCT(current_ts);
	current_ts = (struct task_struct *)((uint32_t)current_ts & 0xfffff000);
	ASSERT(current_ts->kstack_magic == 0x95076400);
	current_ts->elapsed_ticks++;
	ticks++;
	//print_string("ticks is ");
	//print_int((uint32_t)current_ts->ticks);
	//print_string("\r\n");
	if(current_ts->ticks == 0) schedule();
	else 
		current_ts->ticks--;
	//outb(0x20, 0x20);  //OCW2
	//outb(0x20, 0xa0);
	/*asm volatile("movl %ebp, %esp;\
		popl %ebp;\
		iret");*/

	asm volatile("pop %ss;\
		pop %es;\
		pop %ds;\
		popa;\
		leave;\
		iret");
	/*asm volatile("addl $12, %esp;\
		iret");*/
	
}

void(*f[33])(void);

void divd_error(void){
	INTR_HANDLE_SAVE
	print_string("#DE Divid Error.\r\n");
	INTR_HANDLE_RESTORE
}

void debug(void){
	INTR_HANDLE_SAVE
	print_string("#DB Degug.\r\n");
	INTR_HANDLE_RESTORE
}

void nmi_intr(void){
	INTR_HANDLE_SAVE
	print_string("NMI interrupt.\r\n");
	INTR_HANDLE_RESTORE
}

void break_point(void){
	INTR_HANDLE_SAVE
	print_string("#BP Breakpoint.\r\n");
	INTR_HANDLE_RESTORE
}

void over_flow(void){
	INTR_HANDLE_SAVE
	print_string("#OF Overflow.\r\n");
	INTR_HANDLE_RESTORE
}

void bound_exceed(void){
	INTR_HANDLE_SAVE
	print_string("#BR Bound Range Exceeded.\r\n");
	INTR_HANDLE_RESTORE
}

void inval_opcode(void){
	INTR_HANDLE_SAVE
	print_string("#UD Undefined opcode.\r\n");
	INTR_HANDLE_RESTORE
}

void device_not_avl(void){
	INTR_HANDLE_SAVE
	print_string("#NM No math coprocessor.\r\n");
	INTR_HANDLE_RESTORE
}

void double_fault(void){
	INTR_HANDLE_SAVE
	print_string("#DF Double fault.\r\n");
	INTR_HANDLE_RESTORE_WITH_ERROR
}

void math_failure(void){
	INTR_HANDLE_SAVE
	print_string("#MF Coprocessor segment overrun.\r\n");
	INTR_HANDLE_RESTORE
}

void inval_tss(void){
	INTR_HANDLE_SAVE
	print_string("#TS Invalide tss.\r\n");
	INTR_HANDLE_RESTORE_WITH_ERROR
}

void seg_not_present(void){
	INTR_HANDLE_SAVE
	print_string("#NP Segment not preset.\r\n");
	INTR_HANDLE_RESTORE_WITH_ERROR
}

void stack_failure(void){
	INTR_HANDLE_SAVE
	print_string("#SS Stack fault.\r\n");
	INTR_HANDLE_RESTORE_WITH_ERROR
}

void general_protect(void){
	INTR_HANDLE_SAVE
	print_string("#GP General protect.\r\n");
	INTR_HANDLE_RESTORE_WITH_ERROR
}

void page_fault(void){
	INTR_HANDLE_SAVE
	print_string("#PF Page fault.\r\n");
	INTR_HANDLE_RESTORE_WITH_ERROR
}

void reserverd(void){
	INTR_HANDLE_SAVE
	print_string("Reserverd.\r\n");
	INTR_HANDLE_RESTORE
}

void float_error(void){
	INTR_HANDLE_SAVE
	print_string("#MF Math fault.\r\n");
	INTR_HANDLE_RESTORE
}

void align_check(void){
	INTR_HANDLE_SAVE
	print_string("#AC Alignment check.\r\n");
	INTR_HANDLE_RESTORE_WITH_ERROR
}

void machine_check(void){
	INTR_HANDLE_SAVE
	print_string("#MC Machine check.\r\n");
	INTR_HANDLE_RESTORE
}

void simd_fp_exception(void){
	INTR_HANDLE_SAVE
	print_string("#XM SIMD float point exception.\r\n");
	INTR_HANDLE_RESTORE
}

Idtr idr;
Idesc idt[33];

void intr_init(void){
	char * start = "IVT initialization begins.\n\r";
	char * end = "IVT initialization completes.\n\r";

	print_string(start);

	//Initialize IDT
	f[0] = divd_error;
	f[1] = debug;
	f[2] = nmi_intr;
	f[3] = break_point;
	f[4] = over_flow;
	f[5] = bound_exceed;
	f[6] = inval_opcode;
	f[7] = device_not_avl;
	f[8] = double_fault;
	f[9] = math_failure;
	f[10] = inval_tss;
	f[11] = seg_not_present;
	f[12] = stack_failure;
	f[13] = general_protect;
	f[14] = page_fault;
	f[15] = reserverd;
	f[16] = float_error;
	f[17] = align_check;
	f[18] = machine_check;
	f[19] = simd_fp_exception;

	int i = 20;
	for(i; i<32; ++i) { f[i] = f[15];};

	i = 0;
	Idesc *cidt;
	for (i; i< 33; ++i)
	{
		cidt = &idt[i];
		cidt->ihan_low16 = (uint16_t)(((uint32_t)f[i])&0xffff);
		cidt->ihan_sel = (uint16_t)SelectorCode32;
		cidt->iproperty = (uint16_t)0x8e00;
		cidt->ihan_high16 = (uint16_t)(((uint32_t)f[i]>>16)&0xffff);

	}

	cidt = &idt[32];					//Clock interrupt.
	cidt->ihan_low16 = (uint16_t)(((uint32_t)iclock)&0xffff);
	cidt->ihan_sel = (uint16_t)SelectorCode32;
	cidt->iproperty = (uint16_t)0x8e00;
	cidt->ihan_high16 = (uint16_t)(((uint32_t)iclock>>16)&0xffff);

	idr.ivt_len = 8*33;
	idr.ivt_base_low16 = (uint16_t)(((uint32_t)idt)&0xffff);
	idr.ivt_base_high16 = (uint16_t)(((uint32_t)idt>>16)&0xffff);

	asm volatile("lidt %0"::"m"(idr));
	print_string(end);
}

static enum intr_status get_intr_status()
{
	uint32_t eflags;
	GET_EFLAGS(eflags);
	return eflags & EFLAGS_IF?INTR_ON:INTR_OFF;
}

void enable_intr()
{
	if(INTR_ON == get_intr_status())
		return;
	else
		asm volatile("sti");
}

void diable_intr()
{
	if(INTR_OFF == get_intr_status())
		return;
	else
		asm volatile("cli":::"memory");
}

void pic_init(void){
	char * s1 = "PIC initialization statrs.\r\n";
	print_string(s1);
	//ICW1 
	//0|0|0|1|0(LTIM,level/edge triggered mods:0,edge;1,level)|0(ADI,no matter)|0(SNGL:0,sigle;1,cascatded)|1(IC4,wheher need icw4)
	//Master port 0x20, slave port 0xa0
	uint16_t m_port=0x20;
	uint16_t s_port=0xa0;
	uint8_t data=0x11;
	outb(data, m_port);
	outb(data, s_port);

	//ICW2
	//Start verctor number.
	//Master port 0x21, slave port 0xa1.
	uint8_t m_start = 0x20;
	uint8_t s_start = 0x28;
	uint16_t m_port2 = 0x21;
	uint16_t s_port2 = 0xa1;
	outb(m_start, m_port2);
	outb(s_start, s_port2);

	//ICW3
	//User irq2 of master to link slave.
	//Master port 0x21, slave port 0xa1.
	uint8_t m_data = 0x04;
	uint8_t s_data = 0x02;
	outb(m_data, m_port2);
	outb(s_data, s_port2);


	//ICW4
	//0|0|0|0 SFNM(Special Fully Nested Mode:0,fully nested;1,special fully nested)|0 BUF(Buff:1,buff mod)|0 M/S(Rely on "BUF".BUF 1:M/S 1, master;M/S 0, slave;BUF 0,M/S invalid)|1(AEOI, Auto end of interrupt)|1(μPM:0, 8080 or 8085;1,x86)
	uint8_t data2 = 0x03;
	outb(data2, m_port2);
	outb(data2, s_port2);

	//OCW1
	//Mask
	uint8_t data3 = 0xfe;
	uint8_t data4 = 0xff;
	outb(data3, m_port2);
	outb(data4, s_port2);

	char * s2 = "PIC initialization completes.\r\n";
	print_string(s2);

	//OCW2

	//OCW3
}

void en_intr(void)
{
	pic_init();
	intr_init();

}
